package cn.swing.ushare.passport.security;


import cn.swing.ushare.common.web.JsonResult;
import cn.swing.ushare.common.web.State;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * JWT过滤器:从请求头的Authorization中获取JWT中存入的用户信息
 * 并添加到Spring
 */
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    //秘钥
//    @Value("${ushare.jwt.secret-key}")
    private String secretKey = "hnadksjhfndjksfhkdbndsasijhvuhsauhgu";


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Expose-Headers", "Authorization");
        String allowHeaders = "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With, Authorization";
        response.addHeader("Access-Control-Allow-Headers", allowHeaders);
        response.setHeader("Access-Control-Allow-Origin", "*");
        System.out.println("JwtAuthenticationFilter.doFilterInternal()");
        //清楚Spring Security上下文中的数据
        //避免此前曾经存入过的用户信息,后续即使没有携带JWT,在Spring Security仍保存JWT
        SecurityContextHolder.clearContext();
        //客户端提交请求时,必须在请求头的Authentication中添加JWT数据,这是服务器程序的规定,客户端必须执行
        //尝试获取JWT数据
        String jwt = request.getHeader("Authorization");
        System.out.println("从请求头中获取到的JWT=" + jwt);
        //判断是否不存在jwt数据
        if(!StringUtils.hasText(jwt)) {
            // 不存在jwt数据,则放行,后续还有其他过滤器及相关组件进行其他的处理,例如未登录则要求登录等
            //此处不宜直接阻止运行,应为"登录","注册"没有jwt数据}
            System.out.println("请求头中无JWT数据,当前过滤器将放行");
            filterChain.doFilter(request, response);//继续执行过滤器链中后续的过滤器
            return;//必须
        }
        String username = null;
        String permissionsString = null;
        try{
            System.out.println("请求头中包含jwt,准备解析此数据");
            //注意:此时执行时,如果请求头中携带了Authentication,日志中将输出,且不会有任何结果

            Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
            permissionsString = claims.get(JwtConst.KEY_PERMISSIONS).toString();
            username = claims.get(JwtConst.KEY_USERNAME).toString();
            System.out.println("username = " + username);
            System.out.println("permissions = " + permissionsString);
        }catch (ExpiredJwtException e){
            System.out.println("解析JWT失败，此JWT已过期：" + e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    State.ERR_JWT_EXPIRED, "您的登录已过期，请重新登录！");
            String jsonString = JSON.toJSONString(jsonResult);
            System.out.println("响应结果:" + jsonString);
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().println(jsonString);
            return;
        } catch (MalformedJwtException e) {
            System.out.println("解析JWT失败，此JWT数据错误，无法解析：" + e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    State.ERR_JWT_MALFORMED, "获取登录信息失败，请重新登录！");
            String jsonString = JSON.toJSONString(jsonResult);
            System.out.println("响应结果:" + jsonString);
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().println(jsonString);
            return;
        } catch (SignatureException e) {
            System.out.println("解析JWT失败，此JWT签名错误：" + e.getMessage());

            JsonResult<Void> jsonResult = JsonResult.fail(
                    State.ERR_JWT_SIGNATURE, "获取登录信息失败，请重新登录！");
            String jsonString = JSON.toJSONString(jsonResult);
            System.out.println("响应结果:" + jsonString);
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().println(jsonString);
            return;
        } catch (Throwable e) {
            System.out.println("解析JWT失败，异常类型：" + e.getClass().getName());
            e.printStackTrace();
            JsonResult<Void> jsonResult = JsonResult.fail(
                    State.ERR_INTERNAL_SERVER_ERROR, "获取登录信息失败，请重新登录！");
            String jsonString = JSON.toJSONString(jsonResult);
            System.out.println("响应结果:" + jsonString);
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().println(jsonString);
            return;
        }

        //将此前从JWT中读取的permissionsString(Json字符串)转换成Collection<? extends GrantedAuthority>
        List<SimpleGrantedAuthority> permissions = JSON.parseArray(permissionsString, SimpleGrantedAuthority.class);


        // 后续:spring Security发现上下文中有Authentication时,就会视为已登录,甚至可以获取相关信息
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                username, null, permissions
        );
        //将解析得到的用户信息传递给Spring Security
        // 获取Spring Security的上下文，并将Authentication放到上下文中
        SecurityContextHolder.getContext().setAuthentication(authentication);

        //放行
        filterChain.doFilter(request,response);

    }
}
